library("igraph")
library("plotly")
edges <- read.csv( "emails.edges" , header = TRUE, ,sep ='')
edges_with_relation <- edges
edges_with_relation$fromGestor <- edges_with_relation$from < 144
edges_with_relation$toGestor <- edges_with_relation$to < 144
g <- graph.data.frame( edges , directed = TRUE)
g <- simplify(g, edge.attr.comb = "sum")
d <- degree(as.undirected(g)) #Grados de los nodos
max(d) #Grado máximo
min(d) #Grado mínimo
d[which.max(degree(as.undirected(g)))] #Nodo que tiene el grado máximo
# Creación de una tabla con los ID's
nodes_df <- as.data.frame(d)
nodes_df <- cbind(id = rownames(nodes_df), nodes_df)
rownames(nodes_df) <- 1:nrow(nodes_df)
nodes_df$id <- as.numeric(nodes_df$id)
cat("\n Número de empleados\n",length(unique(nodes_df[["id"]])))
Número de empleados
87273
nodes_df$d_in <- as.data.frame(degree(g, mode="in"))$d
nodes_df$d_out <- as.data.frame(degree(g, mode="out"))$d
nodes_df$isGestor <- ifelse(nodes_df$id < 144, 1, 0)
nodes_df <- nodes_df %>%
arrange(nodes_df$id)
fig <- plot_ly(x=nodes_df$d, type = "box", name = "")
fig <- fig %>% layout(yaxis=list(type='linear'))
fig <- fig %>%
layout(title = 'Distribución de los grados de los nodos', plot_bgcolor = "#e5ecf6")
fig <- fig %>% layout(
barmode="group",
bargap=0.2)
fig
nodes_manager_df <- subset(nodes_df, isGestor == 1)
nodes_regular_employee_df <- subset(nodes_df, isGestor == 0)
fig <- plot_ly(x=nodes_manager_df$d, histfunc='sum', type = "histogram", name = "Managers", nbinsx=25)
fig <- fig %>% layout(yaxis=list(type='linear'))
fig <- fig %>%
layout(title = 'Distribución de los grados de managers', plot_bgcolor = "#e5ecf6")
fig <- fig %>% layout(
barmode="group",
bargap=0.2)
fig
fig <- plot_ly(x=nodes_regular_employee_df$d, histfunc='sum', type = "histogram", name = "Regular employees", nbinsx=25)
fig <- fig %>% layout(yaxis=list(type='linear'))
fig <- fig %>%
layout(title = 'Distribución de los grados de empleados regulares', plot_bgcolor = "#e5ecf6")
fig <- fig %>% layout(
barmode="group",
bargap=0.2)
fig
cat("Grado medio de los nodos managers: \t", mean(nodes_manager_df$d))
cat("\nGrado medio de los nodos regulares: \t", mean(nodes_regular_employee_df$d))
cat("\nGrado máximo de los nodos managers: \t", max(nodes_manager_df$d))
cat("\nGrado máximo de los nodos regulares: \t", max(nodes_regular_employee_df$d))
degree
top_degrees <- nodes_df %>%
arrange(desc(d)) %>% head(10)
fig <- plot_ly(
x = as.factor(top_degrees$id),
y = top_degrees$d,
name = "Top 10 nodos con mayor grado",
text = top_degrees$d, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig
Distribución de grados entrantes y salientes
top_degrees_in <- nodes_df %>%
arrange(desc(d_in)) %>% head(10)
fig <- plot_ly(
x = as.factor(top_degrees_in$id),
y = top_degrees_in$d_in,
name = "Top 10 nodos con mayor grado entrante",
text = top_degrees_in$d_in, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig
top_degrees_in <- nodes_df %>%
arrange(desc(d_out)) %>% head(10)
fig <- plot_ly(
x = as.factor(top_degrees_in$id),
y = top_degrees_in$d_out,
name = "Top 10 nodos con mayor grado saliente",
text = top_degrees_in$d_out, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig
fig <- plot_ly(x=nodes_manager_df$d_in, histfunc='sum', type = "histogram", name = "Grados entrantes", nbinsx=25) %>%add_trace(x=nodes_manager_df$d_out, histfunc='sum', type = "histogram", name = "Grados salientes", nbinsx=25)
fig <- fig %>% layout(yaxis=list(type='linear'))
fig <- fig %>%
layout(title = 'Distribución de los grados entrantes/salientes de gestores', plot_bgcolor = "#e5ecf6")
fig <- fig %>% layout(
barmode="group",
bargap=0.2)
fig
fig <- plot_ly(x=nodes_regular_employee_df$d_in, histfunc='sum', type = "histogram", name = "Grados entrantes", nbinsx=25) %>%add_trace(x=nodes_regular_employee_df$d_out, histfunc='sum', type = "histogram", name = "Grados salientes", nbinsx=25)
fig <- fig %>% layout(yaxis=list(type='linear'))
fig <- fig %>%
layout(title = 'Distribución de los grados entrantes/salientes de empleados regulares', plot_bgcolor = "#e5ecf6")
fig <- fig %>% layout(
barmode="group",
bargap=0.2)
fig
Simplificación para medidas estructurales
Dado que no es computacionalmente factible calcular las medidas
estructurales con el grafo de emails.edges original, hemos decidido
simplificar el problema planteando un subgrafo colapsado para un periodo
de tiempo más reducido a partir del fichero emails-timestamps.edges, en
este caso se ha considerado el año 2001.
Medidas estructurales sobre el grafo reducido
edges <- read.csv( "emails-reduced-december-2000.edges" , header = TRUE, ,sep ='')
edges_with_relation <- edges
edges_with_relation$weight <-NULL
edges_with_relation$fromGestor <- edges_with_relation$from < 144
edges_with_relation$toGestor <- edges_with_relation$to < 144
g <- graph.data.frame( edges , directed = TRUE)
g <- simplify(g, edge.attr.comb = "sum")
Error in simplify(g, edge.attr.comb = "sum") :
unused argument (edge.attr.comb = "sum")
diameter(g, directed=TRUE, weight=NA)
diameter(g, directed=FALSE, weight=NA)
#distances(as.undirected(g), weight=NA) # --> Demasiado grande!
mean_distance(as.undirected(g))
#all_shortest_paths(as.undirected(g), from=2, weights=NA) # --> Demasiado grande!
#Componentes del grafo
is.connected(g, mode="weak")
[1] FALSE
is.connected(g, mode="strong")
[1] FALSE
componentes <- components(g, mode="strong")
length(componentes$csize)
[1] 78058
cd <-component_distribution(g)
cmax <- induced.subgraph(g, which(clusters(g)$membership == which.max(clusters(g)$csize)))
articulation.points(g)
+ 4152/87273 vertices, named, from 2927be6:
[1] 9186 13547 11710 11453 10800 1356 2909 19310 10584 20769 26948 1564 17964 18351 22536 21674 1296 11919 403
[20] 12452 12337 268 267 17865 960 11722 12591 5227 13552 13394 14017 11909 11910 14604 3082 3023 27161 27162
[39] 52879 27971 3158 13545 8427 566 19978 3081 19206 17984 31362 66659 574 494 33039 37622 4254 36580 8362
[58] 19073 17222 1420 1454 5758 13802 11807 17427 17376 28250 66707 1012 2356 39524 39543 59698 80041 2301 20344
[77] 41230 80926 19802 18715 18705 42727 6192 36456 30437 85745 33723 3702 71807 12051 28492 5573 35184 472 47929
[96] 48015 49221 48952 48675 47996 7544 49025 48295 48484 48422 48508 48505 21469 26675 23751 21660 25122 21153 21052
[115] 21176 21012 20846 21863 22199 26995 21933 21476 21448 21166 22796 21787 27005 27000 21116 24037 64327 64317 45662
[134] 31666 6720 51266 46235 30122 30302 30216 30206 45701 37476 41631 41719 41615 41624 9914 40025 877 14491 38898
[153] 7654 38706 38694 7630 7655 7615 7638 7632 30756 29313 28398 28703 46628 10214 63777 77597 77674 77621 5492
[172] 3528 59131 4363 74732 48833 73489 74838 33738 57548 57508 57512 57709 60527 80234 80241 45675 8293 80389 82086
+ ... omitted several vertices
max(componentes$csize)
[1] 9164
V(g)$isGestor <- nodes_df$isGestor
#Medidas estructurales
graph.density(as.undirected(g))
transitivity(g)
reciprocity(g)
assortativity.degree(g)
assortativity_nominal(g,as.factor(V(g)$isGestor))
clique.number(g)
cliques(g)
Medidas de centralidad
b <- betweenness(g)
c <- closeness(g)
e <- eigen_centrality(g)$vector
pr <- page_rank(g,directed=FALSE,weights=NA)$vector
nodes_df$b <-b
nodes_df$c <-c
nodes_df$e <-e
nodes_df$pr <-pr
top_betweenness <- nodes_df %>%
arrange(desc(b)) %>% head(5)
fig <- plot_ly(
x = as.factor(top_betweenness$id),
y = top_betweenness$b,
name = "Top 10 nodos con mayor grado",
text = top_betweenness$b, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig <- fig %>%
layout(title = 'Top 5 centralidades de intermediario', plot_bgcolor = "#e5ecf6")
fig
top_closenness <- nodes_df %>%
arrange(desc(c)) %>% head(5)
fig <- plot_ly(
x = as.factor(top_closenness$id),
y = top_closenness$c,
name = "Top 10 nodos con mayor grado",
text = top_closenness$c, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig <- fig %>%
layout(title = 'Top 5 centralidades de cercanía', plot_bgcolor = "#e5ecf6")
fig
top_eigenvector <- nodes_df %>%
arrange(desc(e)) %>% head(5)
fig <- plot_ly(
x = as.factor(top_eigenvector$id),
y = top_eigenvector$e,
name = "Top 10 nodos con mayor grado",
text = top_eigenvector$e, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig <- fig %>%
layout(title = 'Top 5 centralidades de autovector', plot_bgcolor = "#e5ecf6")
fig
top_pagerank<- nodes_df %>%
arrange(desc(pr)) %>% head(5)
fig <- plot_ly(
x = as.factor(top_pagerank$id),
y = top_pagerank$pr,
name = "Top 10 nodos con mayor grado",
text = top_pagerank$pr, # add the text argument
textposition = "inside", # display the text inside the bars
type = "bar"
)
fig <- fig %>%
layout(title = 'Top 5 centralidades de PageRank', plot_bgcolor = "#e5ecf6")
fig
Comunidades
#Comunidades - Girvan-Newman
ebc <- edge.betweenness.community(as.undirected(g))
Warning: At core/community/edge_betweenness.c:493 : Membership vector will be selected based on the highest modularity score.Warning: At core/community/edge_betweenness.c:500 : Modularity calculation with weighted edge betweenness community detection might not make sense -- modularity treats edge weights as similarities while edge betwenness treats them as distances.
NA
plot(ebc, as.undirected(g))
plot_dendrogram(ebc)
#Comunidades - Particiones espectrales
lec <- leading.eigenvector.community(as.undirected(g))
plot(lec, as.undirected(g))
#Comunidades - Louvain
plot(cluster_louvain(as.undirected(g)),as.undirected(g))
#Comunidades - Leiden
plot(cluster_leiden(as.undirected(g),objective_function = "modularity"),as.undirected(g))
LS0tCnRpdGxlOiAiRW1haWxzIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeSgiaWdyYXBoIikKbGlicmFyeSgicGxvdGx5IikKYGBgCgpgYGB7cn0KZWRnZXMgPC0gcmVhZC5jc3YoICJlbWFpbHMuZWRnZXMiICwgaGVhZGVyID0gVFJVRSwgLHNlcCA9JycpCmVkZ2VzX3dpdGhfcmVsYXRpb24gPC0gZWRnZXMKZWRnZXNfd2l0aF9yZWxhdGlvbiRmcm9tR2VzdG9yIDwtIGVkZ2VzX3dpdGhfcmVsYXRpb24kZnJvbSA8IDE0NAplZGdlc193aXRoX3JlbGF0aW9uJHRvR2VzdG9yIDwtIGVkZ2VzX3dpdGhfcmVsYXRpb24kdG8gPCAxNDQKZyA8LSBncmFwaC5kYXRhLmZyYW1lKCBlZGdlcyAsIGRpcmVjdGVkID0gVFJVRSkKZyA8LSBzaW1wbGlmeShnLCBlZGdlLmF0dHIuY29tYiA9ICJzdW0iKQpkIDwtIGRlZ3JlZShhcy51bmRpcmVjdGVkKGcpKSAjR3JhZG9zIGRlIGxvcyBub2RvcwptYXgoZCkgI0dyYWRvIG3DoXhpbW8KbWluKGQpICNHcmFkbyBtw61uaW1vCmRbd2hpY2gubWF4KGRlZ3JlZShhcy51bmRpcmVjdGVkKGcpKSldICNOb2RvIHF1ZSB0aWVuZSBlbCBncmFkbyBtw6F4aW1vCmBgYAoKYGBge3J9CiMgQ3JlYWNpw7NuIGRlIHVuYSB0YWJsYSBjb24gbG9zIElEJ3MKbm9kZXNfZGYgPC0gYXMuZGF0YS5mcmFtZShkKQpub2Rlc19kZiA8LSBjYmluZChpZCA9IHJvd25hbWVzKG5vZGVzX2RmKSwgbm9kZXNfZGYpCnJvd25hbWVzKG5vZGVzX2RmKSA8LSAxOm5yb3cobm9kZXNfZGYpCm5vZGVzX2RmJGlkIDwtIGFzLm51bWVyaWMobm9kZXNfZGYkaWQpCmNhdCgiXG4gTsO6bWVybyBkZSBlbXBsZWFkb3NcbiIsbGVuZ3RoKHVuaXF1ZShub2Rlc19kZltbImlkIl1dKSkpCm5vZGVzX2RmJGRfaW4gPC0gYXMuZGF0YS5mcmFtZShkZWdyZWUoZywgbW9kZT0iaW4iKSkkZApub2Rlc19kZiRkX291dCA8LSBhcy5kYXRhLmZyYW1lKGRlZ3JlZShnLCBtb2RlPSJvdXQiKSkkZApub2Rlc19kZiRpc0dlc3RvciA8LSBpZmVsc2Uobm9kZXNfZGYkaWQgPCAxNDQsIDEsIDApCgpub2Rlc19kZiA8LSBub2Rlc19kZiAlPiUKICBhcnJhbmdlKG5vZGVzX2RmJGlkKQoKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3RfbHkoeD1ub2Rlc19kZiRkLCB0eXBlID0gImJveCIsIG5hbWUgPSAiIikgCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoeWF4aXM9bGlzdCh0eXBlPSdsaW5lYXInKSkKCmZpZyA8LSBmaWcgJT4lCiAgICAgICAgbGF5b3V0KHRpdGxlID0gJ0Rpc3RyaWJ1Y2nDs24gZGUgbG9zIGdyYWRvcyBkZSBsb3Mgbm9kb3MnLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgYmFybW9kZT0iZ3JvdXAiLAogIGJhcmdhcD0wLjIpCgpmaWcKYGBgCgoKYGBge3J9Cm5vZGVzX21hbmFnZXJfZGYgPC0gc3Vic2V0KG5vZGVzX2RmLCBpc0dlc3RvciA9PSAxKQpub2Rlc19yZWd1bGFyX2VtcGxveWVlX2RmIDwtIHN1YnNldChub2Rlc19kZiwgaXNHZXN0b3IgPT0gMCkKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3RfbHkoeD1ub2Rlc19tYW5hZ2VyX2RmJGQsIGhpc3RmdW5jPSdzdW0nLCB0eXBlID0gImhpc3RvZ3JhbSIsIG5hbWUgPSAiTWFuYWdlcnMiLCBuYmluc3g9MjUpIAoKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHlheGlzPWxpc3QodHlwZT0nbGluZWFyJykpCgpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdEaXN0cmlidWNpw7NuIGRlIGxvcyBncmFkb3MgZGUgbWFuYWdlcnMnLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgYmFybW9kZT0iZ3JvdXAiLAogIGJhcmdhcD0wLjIpCgpmaWcKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3RfbHkoeD1ub2Rlc19yZWd1bGFyX2VtcGxveWVlX2RmJGQsIGhpc3RmdW5jPSdzdW0nLCB0eXBlID0gImhpc3RvZ3JhbSIsIG5hbWUgPSAiUmVndWxhciBlbXBsb3llZXMiLCBuYmluc3g9MjUpIAoKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHlheGlzPWxpc3QodHlwZT0nbGluZWFyJykpCgpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdEaXN0cmlidWNpw7NuIGRlIGxvcyBncmFkb3MgZGUgZW1wbGVhZG9zIHJlZ3VsYXJlcycsIHBsb3RfYmdjb2xvciA9ICIjZTVlY2Y2IikKCmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICBiYXJtb2RlPSJncm91cCIsCiAgYmFyZ2FwPTAuMikKCmZpZwpgYGAKCmBgYHtyfQpjYXQoIkdyYWRvIG1lZGlvIGRlIGxvcyBub2RvcyBtYW5hZ2VyczogXHQiLCBtZWFuKG5vZGVzX21hbmFnZXJfZGYkZCkpCmNhdCgiXG5HcmFkbyBtZWRpbyBkZSBsb3Mgbm9kb3MgcmVndWxhcmVzOiBcdCIsIG1lYW4obm9kZXNfcmVndWxhcl9lbXBsb3llZV9kZiRkKSkKCmNhdCgiXG5HcmFkbyBtw6F4aW1vIGRlIGxvcyBub2RvcyBtYW5hZ2VyczogXHQiLCBtYXgobm9kZXNfbWFuYWdlcl9kZiRkKSkKY2F0KCJcbkdyYWRvIG3DoXhpbW8gZGUgbG9zIG5vZG9zIHJlZ3VsYXJlczogXHQiLCBtYXgobm9kZXNfcmVndWxhcl9lbXBsb3llZV9kZiRkKSkKCmRlZ3JlZQoKYGBgCgpgYGB7cn0KdG9wX2RlZ3JlZXMgPC0gbm9kZXNfZGYgJT4lCiAgYXJyYW5nZShkZXNjKGQpKSAlPiUgaGVhZCgxMCkKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3RfbHkoCiAgeCA9IGFzLmZhY3Rvcih0b3BfZGVncmVlcyRpZCksCiAgeSA9IHRvcF9kZWdyZWVzJGQsCiAgbmFtZSA9ICJUb3AgMTAgbm9kb3MgY29uIG1heW9yIGdyYWRvIiwKICB0ZXh0ID0gdG9wX2RlZ3JlZXMkZCwgIyBhZGQgdGhlIHRleHQgYXJndW1lbnQKICB0ZXh0cG9zaXRpb24gPSAiaW5zaWRlIiwgIyBkaXNwbGF5IHRoZSB0ZXh0IGluc2lkZSB0aGUgYmFycwogIHR5cGUgPSAiYmFyIgopCmZpZwpgYGAKCgoKCiMjIyBEaXN0cmlidWNpw7NuIGRlIGdyYWRvcyBlbnRyYW50ZXMgeSBzYWxpZW50ZXMKCgpgYGB7cn0KdG9wX2RlZ3JlZXNfaW4gPC0gbm9kZXNfZGYgJT4lCiAgYXJyYW5nZShkZXNjKGRfaW4pKSAlPiUgaGVhZCgxMCkKCmZpZyA8LSBwbG90X2x5KAogIHggPSBhcy5mYWN0b3IodG9wX2RlZ3JlZXNfaW4kaWQpLAogIHkgPSB0b3BfZGVncmVlc19pbiRkX2luLAogIG5hbWUgPSAiVG9wIDEwIG5vZG9zIGNvbiBtYXlvciBncmFkbyBlbnRyYW50ZSIsCiAgdGV4dCA9IHRvcF9kZWdyZWVzX2luJGRfaW4sICMgYWRkIHRoZSB0ZXh0IGFyZ3VtZW50CiAgdGV4dHBvc2l0aW9uID0gImluc2lkZSIsICMgZGlzcGxheSB0aGUgdGV4dCBpbnNpZGUgdGhlIGJhcnMKICB0eXBlID0gImJhciIKKQpmaWcKYGBgCgpgYGB7cn0KdG9wX2RlZ3JlZXNfaW4gPC0gbm9kZXNfZGYgJT4lCiAgYXJyYW5nZShkZXNjKGRfb3V0KSkgJT4lIGhlYWQoMTApCgpmaWcgPC0gcGxvdF9seSgKICB4ID0gYXMuZmFjdG9yKHRvcF9kZWdyZWVzX2luJGlkKSwKICB5ID0gdG9wX2RlZ3JlZXNfaW4kZF9vdXQsCiAgbmFtZSA9ICJUb3AgMTAgbm9kb3MgY29uIG1heW9yIGdyYWRvIHNhbGllbnRlIiwKICB0ZXh0ID0gdG9wX2RlZ3JlZXNfaW4kZF9vdXQsICMgYWRkIHRoZSB0ZXh0IGFyZ3VtZW50CiAgdGV4dHBvc2l0aW9uID0gImluc2lkZSIsICMgZGlzcGxheSB0aGUgdGV4dCBpbnNpZGUgdGhlIGJhcnMKICB0eXBlID0gImJhciIKKQpmaWcKCmBgYAoKYGBge3J9CmZpZyA8LSBwbG90X2x5KHg9bm9kZXNfbWFuYWdlcl9kZiRkX2luLCBoaXN0ZnVuYz0nc3VtJywgdHlwZSA9ICJoaXN0b2dyYW0iLCBuYW1lID0gIkdyYWRvcyBlbnRyYW50ZXMiLCBuYmluc3g9MjUpICAlPiVhZGRfdHJhY2UoeD1ub2Rlc19tYW5hZ2VyX2RmJGRfb3V0LCBoaXN0ZnVuYz0nc3VtJywgdHlwZSA9ICJoaXN0b2dyYW0iLCBuYW1lID0gIkdyYWRvcyBzYWxpZW50ZXMiLCBuYmluc3g9MjUpIAoKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHlheGlzPWxpc3QodHlwZT0nbGluZWFyJykpCgpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdEaXN0cmlidWNpw7NuIGRlIGxvcyBncmFkb3MgZW50cmFudGVzL3NhbGllbnRlcyBkZSBnZXN0b3JlcycsIHBsb3RfYmdjb2xvciA9ICIjZTVlY2Y2IikKCmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICBiYXJtb2RlPSJncm91cCIsCiAgYmFyZ2FwPTAuMikKCmZpZwpgYGAKCgpgYGB7cn0KZmlnIDwtIHBsb3RfbHkoeD1ub2Rlc19yZWd1bGFyX2VtcGxveWVlX2RmJGRfaW4sIGhpc3RmdW5jPSdzdW0nLCB0eXBlID0gImhpc3RvZ3JhbSIsIG5hbWUgPSAiR3JhZG9zIGVudHJhbnRlcyIsIG5iaW5zeD0yNSkgICU+JWFkZF90cmFjZSh4PW5vZGVzX3JlZ3VsYXJfZW1wbG95ZWVfZGYkZF9vdXQsIGhpc3RmdW5jPSdzdW0nLCB0eXBlID0gImhpc3RvZ3JhbSIsIG5hbWUgPSAiR3JhZG9zIHNhbGllbnRlcyIsIG5iaW5zeD0yNSkgCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoeWF4aXM9bGlzdCh0eXBlPSdsaW5lYXInKSkKCmZpZyA8LSBmaWcgJT4lCiAgICAgICAgbGF5b3V0KHRpdGxlID0gJ0Rpc3RyaWJ1Y2nDs24gZGUgbG9zIGdyYWRvcyBlbnRyYW50ZXMvc2FsaWVudGVzIGRlIGVtcGxlYWRvcyByZWd1bGFyZXMnLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgYmFybW9kZT0iZ3JvdXAiLAogIGJhcmdhcD0wLjIpCgpmaWcKYGBgCgojIyMgU2ltcGxpZmljYWNpw7NuIHBhcmEgbWVkaWRhcyBlc3RydWN0dXJhbGVzCgpEYWRvIHF1ZSBubyBlcyBjb21wdXRhY2lvbmFsbWVudGUgZmFjdGlibGUgY2FsY3VsYXIgbGFzIG1lZGlkYXMgZXN0cnVjdHVyYWxlcyBjb24gZWwgZ3JhZm8gZGUgZW1haWxzLmVkZ2VzIG9yaWdpbmFsLCBoZW1vcyBkZWNpZGlkbyBzaW1wbGlmaWNhciBlbCBwcm9ibGVtYSBwbGFudGVhbmRvIHVuIHN1YmdyYWZvIGNvbGFwc2FkbyBwYXJhIHVuIHBlcmlvZG8gZGUgdGllbXBvIG3DoXMgcmVkdWNpZG8gYSBwYXJ0aXIgZGVsIGZpY2hlcm8gZW1haWxzLXRpbWVzdGFtcHMuZWRnZXMsIGVuIGVzdGUgY2FzbyBzZSBoYSBjb25zaWRlcmFkbyBlbCBhw7FvIDIwMDEuCgoKIyMjIE1lZGlkYXMgZXN0cnVjdHVyYWxlcyBzb2JyZSBlbCBncmFmbyByZWR1Y2lkbwoKCmBgYHtyfQplZGdlcyA8LSByZWFkLmNzdiggImVtYWlscy1yZWR1Y2VkLWRlY2VtYmVyLTIwMDAuZWRnZXMiICwgaGVhZGVyID0gVFJVRSwgLHNlcCA9JycpCmVkZ2VzX3dpdGhfcmVsYXRpb24gPC0gZWRnZXMKZWRnZXNfd2l0aF9yZWxhdGlvbiR3ZWlnaHQgPC1OVUxMCmVkZ2VzX3dpdGhfcmVsYXRpb24kZnJvbUdlc3RvciA8LSBlZGdlc193aXRoX3JlbGF0aW9uJGZyb20gPCAxNDQKZWRnZXNfd2l0aF9yZWxhdGlvbiR0b0dlc3RvciA8LSBlZGdlc193aXRoX3JlbGF0aW9uJHRvIDwgMTQ0CmcgPC0gZ3JhcGguZGF0YS5mcmFtZSggZWRnZXMgLCBkaXJlY3RlZCA9IFRSVUUpCmcgPC0gc2ltcGxpZnkoZywgZWRnZS5hdHRyLmNvbWIgPSAic3VtIikKZCA8LSBkZWdyZWUoYXMudW5kaXJlY3RlZChnKSkgI0dyYWRvcyBkZSBsb3Mgbm9kb3MKbWF4KGQpICNHcmFkbyBtw6F4aW1vCm1pbihkKSAjR3JhZG8gbcOtbmltbwpkW3doaWNoLm1heChkZWdyZWUoYXMudW5kaXJlY3RlZChnKSkpXSAjTm9kbyBxdWUgdGllbmUgZWwgZ3JhZG8gbcOheGltbwpgYGAKCgoKYGBge3J9CmRpYW1ldGVyKGcsIGRpcmVjdGVkPVRSVUUsIHdlaWdodD1OQSkKZGlhbWV0ZXIoZywgZGlyZWN0ZWQ9RkFMU0UsIHdlaWdodD1OQSkKI2Rpc3RhbmNlcyhhcy51bmRpcmVjdGVkKGcpLCB3ZWlnaHQ9TkEpICMgLS0+IERlbWFzaWFkbyBncmFuZGUhCm1lYW5fZGlzdGFuY2UoYXMudW5kaXJlY3RlZChnKSkKI2FsbF9zaG9ydGVzdF9wYXRocyhhcy51bmRpcmVjdGVkKGcpLCBmcm9tPTIsIHdlaWdodHM9TkEpICMgLS0+IERlbWFzaWFkbyBncmFuZGUhCmBgYAoKYGBge3J9CiNDb21wb25lbnRlcyBkZWwgZ3JhZm8KaXMuY29ubmVjdGVkKGcsIG1vZGU9IndlYWsiKQppcy5jb25uZWN0ZWQoZywgbW9kZT0ic3Ryb25nIikKYGBgCgpgYGB7cn0KY29tcG9uZW50ZXMgPC0gY29tcG9uZW50cyhnLCBtb2RlPSJzdHJvbmciKQoKbGVuZ3RoKGNvbXBvbmVudGVzJGNzaXplKQoKY2QgPC1jb21wb25lbnRfZGlzdHJpYnV0aW9uKGcpCgpjbWF4IDwtIGluZHVjZWQuc3ViZ3JhcGgoZywgd2hpY2goY2x1c3RlcnMoZykkbWVtYmVyc2hpcCA9PSB3aGljaC5tYXgoY2x1c3RlcnMoZykkY3NpemUpKSkKYGBgCmBgYHtyfQphcnRpY3VsYXRpb24ucG9pbnRzKGcpCmBgYApgYGB7cn0KbWF4KGNvbXBvbmVudGVzJGNzaXplKQpgYGAKCmBgYHtyfQpWKGcpJGlzR2VzdG9yIDwtIG5vZGVzX2RmJGlzR2VzdG9yCmBgYAoKCmBgYHtyfQojTWVkaWRhcyBlc3RydWN0dXJhbGVzCmdyYXBoLmRlbnNpdHkoYXMudW5kaXJlY3RlZChnKSkKdHJhbnNpdGl2aXR5KGcpCnJlY2lwcm9jaXR5KGcpCmFzc29ydGF0aXZpdHkuZGVncmVlKGcpCmFzc29ydGF0aXZpdHlfbm9taW5hbChnLGFzLmZhY3RvcihWKGcpJGlzR2VzdG9yKSkKYGBgCgpgYGB7cn0KY2xpcXVlLm51bWJlcihnKQpjbGlxdWVzKGcpCmBgYAoKIyMjIE1lZGlkYXMgZGUgY2VudHJhbGlkYWQKCmBgYHtyfQpiIDwtIGJldHdlZW5uZXNzKGcpCmBgYAoKYGBge3J9CmMgPC0gY2xvc2VuZXNzKGcpCmBgYAoKYGBge3J9CmUgPC0gZWlnZW5fY2VudHJhbGl0eShnKSR2ZWN0b3IKYGBgCgpgYGB7cn0KcHIgPC0gcGFnZV9yYW5rKGcsZGlyZWN0ZWQ9RkFMU0Usd2VpZ2h0cz1OQSkkdmVjdG9yCmBgYAoKYGBge3J9Cm5vZGVzX2RmJGIgPC1iCm5vZGVzX2RmJGMgPC1jCm5vZGVzX2RmJGUgPC1lCm5vZGVzX2RmJHByIDwtcHIKYGBgCgpgYGB7cn0KdG9wX2JldHdlZW5uZXNzIDwtIG5vZGVzX2RmICU+JQogIGFycmFuZ2UoZGVzYyhiKSkgJT4lIGhlYWQoNSkKCmZpZyA8LSBwbG90X2x5KAogIHggPSBhcy5mYWN0b3IodG9wX2JldHdlZW5uZXNzJGlkKSwKICB5ID0gdG9wX2JldHdlZW5uZXNzJGIsCiAgbmFtZSA9ICJUb3AgMTAgbm9kb3MgY29uIG1heW9yIGdyYWRvIiwKICB0ZXh0ID0gdG9wX2JldHdlZW5uZXNzJGIsICMgYWRkIHRoZSB0ZXh0IGFyZ3VtZW50CiAgdGV4dHBvc2l0aW9uID0gImluc2lkZSIsICMgZGlzcGxheSB0aGUgdGV4dCBpbnNpZGUgdGhlIGJhcnMKICB0eXBlID0gImJhciIKKQpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdUb3AgNSBjZW50cmFsaWRhZGVzIGRlIGludGVybWVkaWFyaW8nLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcKYGBgCgoKCmBgYHtyfQp0b3BfY2xvc2VubmVzcyA8LSBub2Rlc19kZiAlPiUKICBhcnJhbmdlKGRlc2MoYykpICU+JSBoZWFkKDUpCgpmaWcgPC0gcGxvdF9seSgKICB4ID0gYXMuZmFjdG9yKHRvcF9jbG9zZW5uZXNzJGlkKSwKICB5ID0gdG9wX2Nsb3Nlbm5lc3MkYywKICBuYW1lID0gIlRvcCAxMCBub2RvcyBjb24gbWF5b3IgZ3JhZG8iLAogIHRleHQgPSB0b3BfY2xvc2VubmVzcyRjLCAjIGFkZCB0aGUgdGV4dCBhcmd1bWVudAogIHRleHRwb3NpdGlvbiA9ICJpbnNpZGUiLCAjIGRpc3BsYXkgdGhlIHRleHQgaW5zaWRlIHRoZSBiYXJzCiAgdHlwZSA9ICJiYXIiCikKZmlnIDwtIGZpZyAlPiUKICAgICAgICBsYXlvdXQodGl0bGUgPSAnVG9wIDUgY2VudHJhbGlkYWRlcyBkZSBjZXJjYW7DrWEnLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcKYGBgCgpgYGB7cn0KdG9wX2VpZ2VudmVjdG9yIDwtIG5vZGVzX2RmICU+JQogIGFycmFuZ2UoZGVzYyhlKSkgJT4lIGhlYWQoNSkKCmZpZyA8LSBwbG90X2x5KAogIHggPSBhcy5mYWN0b3IodG9wX2VpZ2VudmVjdG9yJGlkKSwKICB5ID0gdG9wX2VpZ2VudmVjdG9yJGUsCiAgbmFtZSA9ICJUb3AgMTAgbm9kb3MgY29uIG1heW9yIGdyYWRvIiwKICB0ZXh0ID0gdG9wX2VpZ2VudmVjdG9yJGUsICMgYWRkIHRoZSB0ZXh0IGFyZ3VtZW50CiAgdGV4dHBvc2l0aW9uID0gImluc2lkZSIsICMgZGlzcGxheSB0aGUgdGV4dCBpbnNpZGUgdGhlIGJhcnMKICB0eXBlID0gImJhciIKKQpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdUb3AgNSBjZW50cmFsaWRhZGVzIGRlIGF1dG92ZWN0b3InLCBwbG90X2JnY29sb3IgPSAiI2U1ZWNmNiIpCgpmaWcKYGBgCgoKYGBge3J9CnRvcF9wYWdlcmFuazwtIG5vZGVzX2RmICU+JQogIGFycmFuZ2UoZGVzYyhwcikpICU+JSBoZWFkKDUpCgpmaWcgPC0gcGxvdF9seSgKICB4ID0gYXMuZmFjdG9yKHRvcF9wYWdlcmFuayRpZCksCiAgeSA9IHRvcF9wYWdlcmFuayRwciwKICBuYW1lID0gIlRvcCAxMCBub2RvcyBjb24gbWF5b3IgZ3JhZG8iLAogIHRleHQgPSB0b3BfcGFnZXJhbmskcHIsICMgYWRkIHRoZSB0ZXh0IGFyZ3VtZW50CiAgdGV4dHBvc2l0aW9uID0gImluc2lkZSIsICMgZGlzcGxheSB0aGUgdGV4dCBpbnNpZGUgdGhlIGJhcnMKICB0eXBlID0gImJhciIKKQpmaWcgPC0gZmlnICU+JQogICAgICAgIGxheW91dCh0aXRsZSA9ICdUb3AgNSBjZW50cmFsaWRhZGVzIGRlIFBhZ2VSYW5rJywgcGxvdF9iZ2NvbG9yID0gIiNlNWVjZjYiKQoKZmlnCmBgYAoKCiMjIyBDb211bmlkYWRlcwoKYGBge3J9CiNDb211bmlkYWRlcyAtIEdpcnZhbi1OZXdtYW4KZWJjIDwtIGVkZ2UuYmV0d2Vlbm5lc3MuY29tbXVuaXR5KGFzLnVuZGlyZWN0ZWQoZykpCmBgYAoKYGBge3J9CnBsb3QoZWJjLCBhcy51bmRpcmVjdGVkKGcpKQpwbG90X2RlbmRyb2dyYW0oZWJjKQpgYGAKCgpgYGB7cn0KI0NvbXVuaWRhZGVzIC0gUGFydGljaW9uZXMgZXNwZWN0cmFsZXMKbGVjIDwtIGxlYWRpbmcuZWlnZW52ZWN0b3IuY29tbXVuaXR5KGFzLnVuZGlyZWN0ZWQoZykpCnBsb3QobGVjLCBhcy51bmRpcmVjdGVkKGcpKQpgYGAKCmBgYHtyfQojQ29tdW5pZGFkZXMgLSBMb3V2YWluCnBsb3QoY2x1c3Rlcl9sb3V2YWluKGFzLnVuZGlyZWN0ZWQoZykpLGFzLnVuZGlyZWN0ZWQoZykpCmBgYAoKYGBge3J9CiNDb211bmlkYWRlcyAtIExlaWRlbgpwbG90KGNsdXN0ZXJfbGVpZGVuKGFzLnVuZGlyZWN0ZWQoZyksb2JqZWN0aXZlX2Z1bmN0aW9uID0gIm1vZHVsYXJpdHkiKSxhcy51bmRpcmVjdGVkKGcpKQpgYGAKCgoKCg==